import sys
import os
sys.path.append("..")

import random
import Controller.topo_gen as topo_gen

from CoreSim.Simulator import *
from CoreSim.MSchedulerUnicast import *
from CoreSim.MSchedulerBinomial import *
from CoreSim.MSchedulerRing import *
from CoreSim.MSchedulerEPSMulticast import *
from CoreSim.MSchedulerOCSMulticast import *
from CoreSim.MSchedulerJcast import *
from RandomGenerator.Poisson import *
from random import choice

if __name__ == "__main__":

    # EPS/OCS Hybrid Topology
    numHosts  = 10
    numToRs   = 100
    numCores  = 4
    numOCSes  = 4
    link_sh_Bw  = 10.0 * Gb # host to ToR switch and ToR switch to OCS has the same bandwidth
    link_ss_Bw  = 10.0 * Gb #

    # Reconfigure
    update_enable = True    # Update the topology
    interval = 50.0e-1      # Update interval( Seconds)
    delay = 10.0e-3         # Reconfiguration delay

    # multicast traffic
    numDemands = 1
    meanDemands = 10 # The mean of poisson arrival rate of the multicast demand
    flowsNum_a = 1
    flowsNum_b = 1
    
    # flow distribution normal byte unit
    flowSizeUnit_a = M # Byte
    flowSizeUnit_b = G #
    flowSizes = [(250, 2.5)]

    # group distribution
    dstHostSize_a = 2
    dstHostSize_b = 2
    groupSizes = [(3, 3)]
    mflowsDir = ''

    # unicast traffic
    flow_num = 100
    flow_interval = 1000.0
    uflowsDir = ''

    # topo
    topoFileName = "topo_%sOCS_%sToR.txt" %(numOCSes, numToRs)

    #----------------------------------------------------------------------------    
    workloads = ['multicast'] #[('hybird', 'unicast', 'multicast')]
    scheduler = ['unicast', 'binomial', 'ring', 'eps', 'ocs', 'jcast']
    iteration_a = 0
    iteration_b = 1 # run times
    # test case1: one flow, fixed flow size, change group size
    if False:
        mflowsDir = 'groupSize_new/'
        uflowsDir = 'groupSize_new/'

        flowSizeUnit_a = M
        flowSizeUnit_b = M
        flowSizes = [(250, 250)] # 250MB
        
        dstHostSize_a = 5
        dstHostSize_b = 5
        groupSizes = [(3,3), (5,5), (7,7), (9,9), (11,11), (13,13), (15,15), (17,17), (19,19), (21,21)] # 10, 20, 30, 40, 50, 60, 70, 80, 90, 100

        workloads = ['multicast'] #[('hybird', 'unicast', 'multicast')]
        scheduler = ['unicast', 'binomial', 'ring', 'eps', 'ocs', 'jcast']

        iteration_a = 0
        iteration_b = 1 # run 10 times

    # test case2: one flow, fixed group size, change flow size (small flow size)
    if False:
        print 'test case2'
        mflowsDir = 'flowSizeSmall_new/'
        uflowsDir = 'flowSizeSmall_new/'

        dstHostSize_a = 5
        dstHostSize_b = 5
        groupSizes = [(11,11)] # 50
        
        flowSizeUnit_a = K
        flowSizeUnit_b = K
        sample = 16
        flowSizes = []
        for i in xrange(1,sample+1):
            size = 2**i
            flowSizes.append((size, size))
        print flowSizes

        workloads = ['multicast'] #[('hybird', 'unicast', 'multicast')]
        scheduler = ['unicast', 'binomial', 'ring', 'eps', 'ocs', 'jcast']

        iteration_a = 0
        iteration_b = 1 # run 10 times
    
    # test case3: one flow, fixed group size, change flow size (large flow size)
    if False:
        print 'test case3'
        mflowsDir = 'flowSizeLarge_new/'
        uflowsDir = 'flowSizeLarge_new/'

        dstHostSize_a = 5
        dstHostSize_b = 5
        groupSizes = [(11,11)] # 50

        flowSizeUnit_a = M
        flowSizeUnit_b = M
        sample = 20
        flowSizes = []
        for i in xrange(1,sample+1):
            size = i*50
            flowSizes.append((size, size))
        print flowSizes

        workloads = ['multicast'] #[('hybird', 'unicast', 'multicast')]
        scheduler = ['unicast', 'binomial', 'ring', 'eps', 'ocs', 'jcast']

        iteration_a = 0
        iteration_b = 1 # run 10 times

    # test case4: multicast flow and unicast flow
    if False:
        mflowsDir = 'Hybrid_new/'
        uflowsDir = 'Hybrid_new/'

        flowSizeUnit_a = M
        flowSizeUnit_b = M
        flowSizes = [(100, 1000)]
        flowsNum_a = 50
        flowsNum_b = 50
        
        dstHostSize_a = 1
        dstHostSize_b = 5
        groupSizes = [(3,11)] # 10, 20, 30, 40, 50, 60, 70, 80, 90, 100

        workloads = ['hybird', 'multicast'] #[('hybird', 'unicast', 'multicast')]
        scheduler = ['unicast', 'binomial', 'ring', 'eps', 'ocs', 'jcast']

        # unicast flows
        flow_num = 10000
        flow_interval = 100.0

        # runtimes
        iteration_a = 0
        iteration_b = 1 # run 10 times

    # test case5: multicast flow and unicast flow
    if False:
        print 'test case5'
        mflowsDir = 'Hybrid_multicast/'
        uflowsDir = 'Hybrid_multicast/'

        flowSizeUnit_a = M
        flowSizeUnit_b = M
        flowSizes = [(100, 1000)]
        flowsNum_a = 50
        flowsNum_b = 50
        
        dstHostSize_a = 1
        dstHostSize_b = 5
        groupSizes = [(3,11)] # 10, 20, 30, 40, 50, 60, 70, 80, 90, 100

        workloads = ['hybird'] #[('hybird', 'unicast', 'multicast')]
        scheduler = ['eps', 'ocs', 'jcast']

        # unicast flows
        flow_num = 10000
        flow_interval = 100.0

        # runtimes
        iteration_a = 0
        iteration_b = 10 # run 10 times

    if False:
        print 'test case6 -- small unicast flow interval'
        mflowsDir = 'Hybrid_multicast1/'
        uflowsDir = 'Hybrid_multicast1/'

        flowSizeUnit_a = M
        flowSizeUnit_b = M
        flowSizes = [(100, 1000)]
        flowsNum_a = 50
        flowsNum_b = 50
        
        dstHostSize_a = 1
        dstHostSize_b = 5
        groupSizes = [(3,11)] # 10, 20, 30, 40, 50, 60, 70, 80, 90, 100

        workloads = ['hybird', 'multicast'] #[('hybird', 'unicast', 'multicast')]
        #scheduler = ['eps', 'ocs', 'jcast']
        scheduler = ['unicast', 'binomial', 'ring']

        # unicast flows
        flow_num = 10000
        flow_interval = 1000.0

        # runtimes
        iteration_a = 1
        iteration_b = 10 # run 10 times

    # same as test case5: run 10 times
    if False:
        numHosts  = 10
        numToRs   = 300
        numCores  = 12
        numOCSes  = 12

        mflowsDir = 'ControllerOverhead/'
        uflowsDir = 'ControllerOverhead/'
        flowSizes = [(100, 1000)] # MB
        groupSizes = [(3, 11)]    # 10 ~ 100
        dstHostSize = 5
        flowsNum_a = 600
        flowsNum_b = 600
        flowSizeUnit_a = MB
        flowSizeUnit_b = MB

        workloads = ['multicast'] #[('hybird', 'unicast', 'multicast')]
        scheduler = ['ocs']

        iteration_a = 0
        iteration_b = 1 # run 10 times

    # test case6: one flow, fixed group size, change flow size (small flow size)
    if False:
        mflowsDir = 'flowSizeSmallByte/'
        groupSizes = [(4,4)] # 50
        sampe = 16
        flowSizes = []
        for x in xrange(1,sampe+1):#KB
            size = 2**x
            flowSizes.append((size, size))
        print flowSizes
        workloads = ['multicast']
        scheduler = ['binomial', 'ring']
    
    for i in xrange(iteration_a, iteration_b):
        for (groupSize_a, groupSize_b) in groupSizes: # different groupSize
            print (groupSize_a, groupSize_b)
            for (flowSize_a, flowSize_b) in flowSizes: # different flowSize
                print (flowSize_a, flowSize_b)
                
                # 1. Generate multicast traffic
                mflowsFileName = "%sCore_%sOCS_%sToR_%sHost_%sDemand_%sFlows_%s%sSize_%s%sGroup_%s.txt"\
                            %(numCores, numOCSes, numToRs, numHosts, numDemands, flowsNum_b, int(flowSize_a), int(flowSize_b), int(groupSize_a), int(groupSize_b), i)
                mflowsFileName = mflowsDir+mflowsFileName
                flowSize_a *= flowSizeUnit_a
                flowSize_b *= flowSizeUnit_b
                if not os.path.exists(inDir+mflowsDir):
                    os.makedirs(inDir+mflowsDir)
                
                if not os.path.isfile(inDir+mflowsFileName):
                    f_name = inDir + mflowsFileName
                    f = open(f_name, "w")
                    startTime = 0.0
                    flwoID = 0
                    randPoisson = PoissonRand(mean=meanDemands, bound=1.0/meanDemands * 10.0)
                    for demand_i in xrange(0,numDemands):
                        flowsNum = int(random.uniform(flowsNum_a, flowsNum_b))
                        #print "flowsNum:\t%d\tstartTime\t%f" % (flowsNum, startTime)
                        print >> f, "flowsNum:\t%d\tstartTime\t%f" % (flowsNum, startTime)
                        startTime += randPoisson.GetPoissonNumber()
                        for flows_i in xrange(0,flowsNum):
                            flwoID += 1
                            flowSize = int(random.uniform(flowSize_a, flowSize_b))
                            groupSize = int(random.uniform(groupSize_a, groupSize_b))
                            groupToRs = random.sample(xrange(1, numToRs+1), groupSize)
                            groupToRs.sort()
                            # print 'group %s'%flows_i, flowSize, groupSize
                            # print groupToRs
                            srcToR = random.choice(groupToRs)
                            groupToRs.remove(srcToR)
                            dstToRs = groupToRs
                            srcHost = random.randint(1, numHosts)
                            dstHosts = ''
                            for dstToR in dstToRs:
                                dstHosts += '%s:'%dstToR
                                dstHostSize = int(random.uniform(dstHostSize_a, dstHostSize_b))
                                dstHosts_temp = random.sample(xrange(1, numHosts+1), dstHostSize)
                                dstHosts_temp.sort()
                                for dstHost_i in range(0, len(dstHosts_temp)-1):
                                    dstHosts += '%s,' %dstHosts_temp[dstHost_i]
                                dstHosts += '%s '%dstHosts_temp[len(dstHosts_temp)-1]
                            print >> f, "%d\t%d\t%d:%d\t%s"%(flwoID, flowSize, srcToR, srcHost, dstHosts)
                    f.close()
                
                # 2. Generate unicast traffic
                uflowsFileName = "%sToR_%sHost_%sFlows_%sInterval_%s.txt" %(numToRs, numHosts, flow_num, int(flow_interval), i)
                uflowsFileName = uflowsDir+uflowsFileName
                if not os.path.exists(inDir+uflowsDir):
                    os.makedirs(inDir+uflowsDir)
                if not os.path.isfile(inDir+uflowsFileName):
                    # the percentage of flows of different sizes under uniform distribution KB
                    uniform_percent = {"10000": 0.12, "20000": 0.13, "30000": 0.12, "50000": 0.13, "100000": 0.12, "201070": 0.13, "310700": 0.12, "600000": 0.13}
                    # initialize current time
                    current_time = 0.0
                    
                    f_name = inDir + uflowsFileName
                    f = open(f_name, "w")

                    fsizes = uniform_percent.keys()
                    fnum_of_fsizes = [int(x*flow_num) for x in uniform_percent.values()]
                    
                    counter = 0
                    while any(fnum_of_fsizes):
                        index = counter % len(fsizes)
                        if fnum_of_fsizes[index]:
                            fsize = int(fsizes[index])
                            fnum_of_fsizes[index] -= 1

                            srcHostId = choice(range(numToRs * numHosts))+1
                            dstHostId = choice(range(numToRs * numHosts))+1
                            srcToRId = GetToRId(srcHostId, numHosts)
                            dstToRId = GetToRId(dstHostId, numHosts)
                            while srcToRId == dstToRId:
                                srcHostId = choice(range(numToRs * numHosts))+1
                                dstHostId = choice(range(numToRs * numHosts))+1
                                srcToRId = GetToRId(srcHostId, numHosts)
                                dstToRId = GetToRId(dstHostId, numHosts)
                            
                            # compute arrival time of current flow, arrival_time_delta is the arrival interval of the current flow
                            arrival_time_delta = choice(range(1, 5)) / flow_interval
                            current_time += arrival_time_delta

                            # flow_size
                            fsize_delta_factor = choice(range(100)) / 100.0
                            flowSize = fsize * 1024 * (1.0 + fsize_delta_factor) # Byte
                            #print srcHostId, dstHostId, current_time, flowSize
                            print >> f, "%d\t%d\t%f\t%f"%(srcHostId, dstHostId, current_time, flowSize)
                        counter += 1
                    f.close()

                # 3. Generate hybird topo
                topoFileName = "topo_%sOCS_%sToR.txt" %(numOCSes, numToRs)
                f_name = inDir + topoFileName
                if not os.path.isfile(f_name):
                    f = open(f_name, "w")
                    match_results = topo_gen.k_regular_network(numToRs, numOCSes, False)
                    #print match_results
                    for k in match_results:
                        print >> f, "ocs %s" %k
                        for src, dst in match_results[k]:
                            dst -= numToRs
                            print >> f, "%d\t%d\t%d" %(src, dst, link_sh_Bw)
                    f.close()

                # 4. Initial Simulation
                for workload in workloads:
                    if workload != 'unicast':
                        #scheduler = ['ring'] # ['unicast', 'binomial', 'ring', 'eps', 'ocs', 'jcast']
                        
                        for sched in scheduler:
                            sim = Simulator()
                            topo_args = (numHosts, numToRs, numCores, numOCSes, link_sh_Bw, link_ss_Bw)
                            flow_args = (mflowsFileName, uflowsFileName, workload)
                            conf_args = (interval, delay, update_enable)
                            print '------------------------ %s ------------------------' %sched
                            if sched == 'unicast':
                                sim.sched = MSchedulerUnicast(topo_args, flow_args, conf_args)
                            elif sched == 'binomial':
                                sim.sched = MSchedulerBinomial(topo_args, flow_args, conf_args)
                            elif sched == 'ring':
                                sim.sched = MSchedulerRing(topo_args, flow_args, conf_args)
                            elif sched == 'eps':
                                sim.sched = MSchedulerEPSMulticast(topo_args, flow_args, conf_args)
                            elif sched == 'ocs':
                                sim.sched = MSchedulerOCSMulticast(topo_args, flow_args, conf_args)
                            elif sched == 'jcast':
                                sim.sched = MSchedulerJcast(topo_args, flow_args, conf_args)

                            if workload == 'hybird':
                                sim.sched.AssignUnicast(sim.eventQueue)

                            sim.sched.InitialTopology(topoFileName)
                            sim.sched.AssignMulticast()
                            sim.sched.InitialMulticast(sim.eventQueue)
                            
                            if update_enable:
                                event = UpdateTopologyEvent(interval) # reconfigure event
                                sim.eventQueue.push(event, event.time)
                            
                            # 5. Start Simulation
                            sim.Run()
                            sim.sched.PrintFlows()
                    else:
                        sim = Simulator()
                        sim.sched = MSchedulerUnicast(topo_args, flow_args, conf_args)
                        sim.sched.AssignUnicast(sim.eventQueue)
                        sim.sched.InitialTopology(topoFileName)
                        sim.sched.AssignMulticast()
                        sim.sched.InitialMulticast(sim.eventQueue)

                        if update_enable:
                            event = UpdateTopologyEvent(interval) # reconfigure event
                            sim.eventQueue.push(event, event.time)
                        sim.Run()
                        sim.sched.PrintFlows()


